<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>区间复合平均价法（次低价平均）</title>
    <style type="text/css">
        #barChart {
            position: absolute;
            /*width: 400px;*/
            height: 200px;
            right: 10px;
            top: 10px;
        }

        /*柱状图样式*/
        .chart rect {
            stroke: white;
            fill: steelblue;
        }

        .chart rect.gray {
            fill: gray;
        }

        .chart rect:hover {
            fill: brown;
        }

        #main input {
            width: 1.5em;
            text-align: right;
        }

        /*控制坐标轴样式*/
        .axis path,
        .axis line {
            fill: none;
            stroke: black;
            shape-rendering: crispEdges;
        }

        .axis text {
            font-family: sans-serif;
            font-size: 11px;
        }

        .axis g text {
            font-family: sans-serif;
            font-size: 11px;
            color: black;
        }

        .axis.red {
            color: red;
        }

        .red {
            color: red;
        }

        .green {
            color: green;
        }

        input#otherLineHigh, input#otherLineLow {
            width: 2em;
        }
    </style>
    <script src="jquery.js"></script>
    <script src="underscore-min.js"></script>
    <script type="text/javascript">
        var stdout = function (x) {
            console.log(x);
            $("#ans").append("<br>" + x);
        }
        $(document).ready(function () {
            $("form").submit(function () {
                event.preventDefault();
            });
            $("#deal").click(dealAndShow);
            $("#animi").click(function () {
                allAnimiAns = [];
                curTestData = parseFloat($("#otherLineHigh").val()) || curTestData;
                curTestDataLower = parseFloat($("#otherLineLow").val()) || curTestDataLower;
                ourData = $("textarea").val().split("\n").map(function (x, i) {
                    return parseFloat(x) || 0;
                });
                showAnima();
            });
        });
        var curTestData = 600;
        var curTestDataLower = 400;
        var allAnimiAns = [];
        var ourData = [535, 546, 521, 543];

        function showAnima() {
            setTimeout(function () {
                var ourD = ourData.slice(0);
                ourD.push(curTestData);
                if (curTestData >= curTestDataLower) {
                    dealAndShow({data: ourD});
                    curTestData -= 1;
                    showAnima();
                }
                else {
                    $("#ans").html(allAnimiAns.join('<br>'));
                }
            }, 60);
        }

        function dealAndShow(ds) {
            dealDatas(ds);

            D3Data = datas.map(function (x, i) {
                var pointToShow = (points[i] == 0) ? -i : points[i];
                return {Downtime: x, EquipName: pointToShow, index: i};
            });
            //BarChart(D3Data.sort(function(a,b){return a.EquipName - b.EquipName}));
            BarChart(D3Data.sort(function (a, b) {
                return a.EquipName - b.EquipName
            }));
            //event.preventDefault();
        }

        var datas = [];
        var orgdatas = [];
        var points = [];
        var A, A1;
        var highLine, lowLine, mLineHigh, mLineLow, nLine;

        function calcDatas__0() {
            //判定个数是否在[20,30)区间,在的话去除两个最高分一个最低分再计算
            A4 = average(datas);
            if (datas.length < 10) {
                stdout("当前配置下,数据量小于10,请确认数据量");
                //return;
            }
            else if (datas.length >= 10 && datas.length < 20) {
                stdout("当前配置下,数据量大于等于10小于20,请确认数据量");
                //return;
            }
            else if (datas.length >= 20 && datas.length < 30) {
                stdout("当前配置下,数据量大于等于20小于30,请确认数据量");
            }
            else {
                stdout("当前配置下,数据量大于等于30,请确认数据量");
            }
            orgdatas = datas;
            var tmpMin = datas.sort()[0];
            if (datas.length < 10) {
                datas = datas.sort().slice(0);
            }
            else {
                datas = datas.sort().slice(1);
                var tmpMax0 = datas.pop();
                if (orgdatas.length >= 20) {
                    var tmpMax1 = datas.pop();
                    if (orgdatas.length >= 30) {
                        var tmpMax2 = datas.pop();
                        stdout('数据 ' + tmpMax0 + " " + tmpMax1 + " " + tmpMax2 + " " + tmpMin + ' 为三个最高分和一个最低分,已经在后续计算中去除');
                    } else {
                        stdout('数据 ' + tmpMax0 + " " + tmpMax1 + " " + tmpMin + ' 为两个最高分和一个最低分,已经在后续计算中去除');
                    }
                }
                else if (orgdatas.length >= 10 && orgdatas.length < 20) {
                    stdout('数据 ' + tmpMax0 + " " + tmpMin + ' 为一个最高分和一个最低分,已经在后续计算中去除');
                }
            }

            A = calc(datas);
            if (A == -1) {
                return;
            }
            var ans = calcAns(orgdatas, A);
            points = ans;
            return ans;
        }

        function dealDatas(ds) {
            lowLine = 1 - parseFloat($("#lowerLine").val()) / 100;
            highLine = 1 + parseFloat($("#higherLine").val()) / 100;
            nLine = parseFloat($("#nLine").val()) || 1;
            mLineHigh = parseFloat($("#mLineHigh").val()) || 1;
            mLineLow = parseFloat($("#mLineLow").val()) || 0.3;

            $("#ans").text($("textarea").val());
            if (ds && ds.data) {
                datas = ds.data;
            }
            else {
                datas = _.map($("textarea").val().split("\n"), function (x, i) {
                    return parseFloat(x) || 0;
                });
            }
            for (var i = 0; i < datas.length; i++) {
                if (datas[i] == 0) {
                    alert("数据里面有空行,请检查后重试");
                    return;
                }
            }
            var ans = calcDatas__0();

            var anstext = [];
            var sortresult = [];
            for (var i = 0; i < ans.length; i++) {
                anstext.push(datas[i] + "  :  " + ans[i]);
                sortresult.push([datas[i], ans[i]]);
            }
            //allAnimiAns.push(sortresult.sort(function(a,b){return a[1]> b[1];}).map(function(x){
            allAnimiAns.push(_.map(_.sortBy(sortresult, function (a) {
                return a[1];
            }), function (x) {
                var classN = curTestData!= x[0] ? 'class="green"' : 'class="red"';
                return '<span ' + classN + '>' + x[0] +  ' (' + x[1].toFixed(2) + ') ' +'</span>';
            }).join(', '));
            $("#ans").append('<br>' + ans.join("  ") + '<hr>最终分数<br/>' + anstext.join("<br/>"));
        }

        function calcAns(data, A) {
            return data.map(function (x, i) {
                if (x >= A) {
                    m = mLineHigh;   //评标价格高于或等于基准价格
                }
                else {
                    m = mLineLow;  //评标价格低于基准价格
                }
                var n = nLine;
                /*
                //超出区间直接取分数为0分
                if(x > A*highLine || x<A*lowLine){
                return 0;
                }*/
                var res = 100 - 100 * m * n * Math.abs(x - A) / A;
                res = res < 0 ? 0 : res;
                return Math.floor(res * 1000) / 1000; //精确到小数点后面三位
            });
        }

        function calc(data) {
            var leng = data.length;
            stdout(data);
            A1 = average(data);
            var N = data.length;
            //最高价448.5
            stdout("平均值A1 (N个单位): " + A1 + " (" + N + "个单位)");
            stdout("取值区间:" + A1 * lowLine + "  " + A1 * highLine);
            var matchData = []; //在A1 区间 -20%~ 10%以内的
            var notMatchData = [];
            for (var i = 0; i < data.length; i++) {
                var curData = data[i];
                if (isMatch(curData, A1)) {
                    matchData.push(curData);
                }
                else {
                    notMatchData.push(curData);
                }
            }

            var A2 = average(matchData);
            var P = matchData.length;
            stdout("平均值A2 : " + A2 + " (保留" + P + "个竞标厂家)");
            stdout("被保留的值: " + matchData.join(" "));

            if (P == 0) {
                stdout("合适区间内无数据,平均值A与A4相同: " + A4);
                return A4;
            }

            var A3 = (A2 + listMin(matchData)) / 2;
            stdout("平均值A3: " + A3);
            return A3;
        }

        /** 判断价格是否在合理竞价区间内 **/
        /* 区间 -0.2<=x<= +0.1 **/
        /* 区间 -0.20<=x<= +0.10 */
        function isMatch(value, average) {
            return (value >= average * lowLine) && (value <= average * highLine);
        }


        /** 求数组平均值 **/
        function average(data) {
            if (data.length == 0) {
                return -1;
            }
            var sum = 0;
            for (var i = 0; i < data.length; i++) {
                sum += data[i];
            }
            return sum / data.length;
        }

        /** 求最大值 **/
        function listMax(data) {
            var max = data[0];
            for (var i = 1; i < data.length; i++) {
                if (data[i] > max) {
                    max = data[i];
                }
            }
            return max;
        }

        /** 求最小值 **/
        function listMin(data) {
            var min = data[0];
            for (var i = 1; i < data.length; i++) {
                if (data[i] < min) {
                    min = data[i];
                }
            }
            return min;
        }

        /** 创建随机数组 **/
        function creatRandomList(size, maxNum, minNum) {
            minNum = minNum || 0;
            maxNum = (maxNum || 100) - minNum;
            var ans = [];
            for (var i = 0; i < size; i++) {
                ans.push(Math.floor(Math.random() * maxNum) + minNum);
            }
            return ans;
        }
    </script>
    <script type="text/javascript" src="d3/d3.min.js"></script>
    <script type="text/javascript">
        //柱状图表js,我下面的东西都是一块一块的，用上哪段就能得到想要的效果
        var D3Data = [
            {EquipName: 'ssss', Downtime: 1},
            {EquipName: 'ssscs', Downtime: 3},
            {EquipName: 'sssxxs', Downtime: 4},
            {EquipName: 'ssxxxss', Downtime: 2},
            {EquipName: 'xxssss', Downtime: 8},
            {EquipName: 'ssssff', Downtime: 2},
            {EquipName: 'ssasss', Downtime: 3},
            {EquipName: 'ssssss', Downtime: 6},
        ];

        function BarChart(data) {
            //?d3.scale.ordinal()和他们的domain,,,translate，dy属性

            // 1、清空图表区内的图表,重新生成
            $('#barChart').html("");

            // 2、定义初始化数据，图表的上下左右距离和宽高,连续定义
            var margin = {top: 60, right: 30, bottom: 70, left: 50},
                width = 600 - margin.left - margin.right,
                height = 400 - margin.top - margin.bottom;

            // 3、创建容器并移动位置到合适(注意：这点因为append了一个g所以返回的svg其实是这个g，后面的元素都在这里，定位也是相对于他)
            svg = d3.select("div#barChart").append("svg")
                .attr("class", "chart")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            // 4、定义计算X轴的线性比例尺的方法
            //散列值 把width平均分配到data的每个数据段（比如是6个） 0~80，80~160,...值为（0，80，160，...）域宽80
            var xScale = d3.scale.ordinal()
                .domain(data.map(function (d) {
                    return d.EquipName;
                }))
                //.rangeRoundBands([0,width], .1,1);//两种效果不一样
                .rangeRoundBands([0, width], 0.1);

            // 5、定义计算Y轴的比例尺的方法
            var yScale = d3.scale.linear()
                .domain([0, d3.max(data, function (d) {
                    return d.Downtime;
                })])
                .range([height, 0]);

            // 6、创建X坐标轴函数
            var xAxis = d3.svg.axis()
                .scale(xScale)
                .orient("bottom");

            // 7、创建Y轴坐标轴函数
            var yAxis = d3.svg.axis()
                .scale(yScale)
                .orient("left");

            // 8、创建柱状图（注意：貌似定位都是相对于左上角）
            svg.selectAll(".barRect").data(data).enter().append("rect")
                .attr("class", "barRect")
                .attr("x", function (d) {
                    return xScale(d.EquipName);
                })//相当于function(d){return xScale(d);}
                //.attr("x", function(d, i) {return i * (width / data.length);})
                .attr("y", function (d) {
                    return yScale(d.Downtime);
                })//svg的坐标以左上角为原点,通过高度运算转成原点在左下角的效果
                .attr("width", xScale.rangeBand()) //获取散列值每段的长度 为矩形的宽
                //.attr("width", width / data.length - 10) //获取散列值每段的长度 为矩形的宽
                .attr("height", function (d) {
                    return height - yScale(d.Downtime);
                })// 通过函数yScale  function(d){return  (420/42)*d}  得到矩形的高
                .attr("class", function (d) {
                    return (d.Downtime > A1 * highLine || d.Downtime < A1 * lowLine) ? "barRect gray" : "barRect";
                });

            // 9、加背景横线
            svg.selectAll("line")
                .data(yScale.ticks(10))   //y.ticks 根据权重 把数据进行划分层次，增加可读性。可以自己改变ticks的值察看效果来理解
                .enter().append("line")   //画线 （x1,y1） ------> (x2,y2)
                .attr("x1", 0)
                .attr("x2", width)
                .attr("y1", function (d) {
                    return height - yScale(d);
                })
                .attr("y2", function (d) {
                    return height - yScale(d);
                })
                .style("stroke", "#ccc");


            // 10、加图表标题
            svg.append("text")
                .attr("x", (width / 2))
                .attr("y", -40)
                .attr("text-anchor", "middle")
                .style("font-size", "16px")
                .style("text-decoration", "underline")
                .text("报价比较趋势图");

            // 11、添加矩形上方的数字
            svg.selectAll(".label")
                .data(data)
                .enter().append("text")
                .attr("class", "label")
                .attr("x", function (d) {
                    return xScale(d.EquipName) + xScale.rangeBand() / 2;
                })  //散列值+散列宽度的一半
                .attr("y", function (d) {
                    return yScale(d.Downtime);
                })
                .attr("dx", ".35em") //  horizontal-align: middle 居中对齐
                .attr("dy", 0) // vertical-align: middle //垂直方向无偏移
                .attr("text-anchor", "end") // text-align: right
                .text(function (d) {
                    return d.Downtime;
                });
            //.text(String); //设置数据为显示值 相当于.text(function(d){ return d;}


            // 最后加坐标轴否则影响很多东西
            // 99、添加X坐标轴及坐标轴名称
            svg.append("g")
                .attr("class", "axis")
                .attr("transform", "translate(0," + height + ")")
                .call(xAxis)
                .append("text")
                .attr("transform", "translate(" + (width) + "," + 20 + ")")
                .style("text-anchor", "middle")
                .text("得分(升序排列)");

            // 如果x轴名称太长将其倾斜
            svg.selectAll(".axis g text")
                .style("text-anchor", "end")
                .attr("dx", "-.8em")
                .attr("dy", ".15em")
                .attr("transform", "rotate(-35)");

            // 100、添加Y坐标轴及坐标轴名称
            svg.append("g")
                .attr("class", "axis")
                .call(yAxis)
                .append("text")
                .attr("transform", "rotate(0)")
                .attr("x", 50)
                .attr("y", -20)
                .attr("dy", ".71em")
                .style("text-anchor", "end")
                .text("报价");


            svg.append("g")
                .attr("class", "axis red")
                .call(yAxis)
                .append("text")
                .attr("transform", "rotate(0)")
                .attr("x", 0)
                .attr("y", yScale(A))
                .attr("dy", ".71em")
                //.style("text-anchor", "end")
                .text("基准价");

            svg.append("g")
                .attr("class", "axis red")
                .call(yAxis)
                .append("text")
                .attr("transform", "rotate(0)")
                .attr("x", 0)
                .attr("y", yScale(A1 * lowLine))
                .attr("dy", ".71em")
                //.style("text-anchor", "end")
                .text("下限")
                .style("color", "red");

            svg.append("g")
                .attr("class", "axis red")
                .call(yAxis)
                .append("text")
                .style("color", "red")
                .attr("transform", "rotate(0)")
                .attr("x", 0)
                .attr("y", yScale(A1 * highLine))
                .attr("dy", ".71em")
                //.style("text-anchor", "end")
                .text("上限");

            svg.selectAll("line .redline")
                .data([A1 * lowLine, A, A1 * highLine])
                .enter().append("line")
                .attr("x1", 0)
                .attr("x2", width)
                .attr("y1", function (d) {
                    return yScale(d);
                })
                .attr("y2", function (d) {
                    return yScale(d);
                })
                .style("stroke", "red");
        }
    </script>
    <script>
        var foo = function (value) {
            console.log('foo:' + value.x + "  " + value.y);
            //改成自己的实际代码
        }
        var controlCenter = $.Callbacks();
        controlCenter.add(foo);

        window.onresize = function () {
            var winH = $(window).height();
            var footH = 100; //todo:等待改为实际代码
            controlCenter.fire({x: winH, y: footH});
        }

    </script>
</head>
<body>
<h2>竞价报价预估算工具</h2>
<div id="main">
    <form>
        <textarea rows="10" cols="20" id=" datas">242
200
220
300
150
230
210</textarea>
        <br/>
        <button type="button" id="deal">计算</button>
        <span>&nbsp;下限-</span><input id="lowerLine" value=20>% <span>&nbsp;上限+</span><input id="higherLine" value=10>%
        <span> &nbsp;n = </span><input id="nLine" value=1>
        <span> &nbsp;m竞标价高于基准价时</span><input id="mLineHigh" value=1> &nbsp;m竞标价低于基准价时</span><input id="mLineLow"
                                                                                                   value=0.5><br/>
        <button type="button" id="animi">N控1</button>
        <span> &nbsp;对方上限</span><input id="otherLineHigh" value=250> &nbsp;对方下限</span><input id="otherLineLow"
                                                                                             value=200>
        <br/>
        <div id="ans">&nbsp;【区间复合平均价法（次低价平均）：<br>
            M：为进入详评的投标人数量 a．如果M<10，不去掉任何一个报价<br>
            b．如果10<=M<20，去掉一个最高评标价和一个最低评标价，如果如在同一包内出现并列最高或最低评标价的情况，在计算基准价时只去掉其中一个最高或最低评标价，以下类推<br>
            c．如果20<=M<30，则去掉两个最高评标价和一个最低评标价 <br>
            d．如果30<=M，则去掉三个最高评标价和两个最低评标价； 然后计算剩余投标人（N个）评标价的算术平均值A1；<br>
            再剔除（剔除只为计算平均值使用，并不是废标）评标价与算术平均值A1 偏差超过[-20%，10%]区间的投标人报价，计算剔除后投标人（P个）评标价的算术平均值A2；<br>
            最后计算A2和P个投标人中最低评标价的算术平均值A3，以A3为基准价； 若N个评标价均在算术平均值A1 [-20%，10%]区间以外，则所有进入详评的投标人（M个）评标价的算术平均值A4作为基准价。<br>
            价格部分得分=100-100×n×m×|投标人的评标总价-基准价|/基准价； 当投标人的评标总价>=基准价，m=1； 当投标人的评标总价 基准价 ，m=[0.3，0.8]；
            当计算出的价格部分得分<0，按0分计。<br>
        </div>
    </form>
    <div id="barChart"></div>
</div>
</body>
</html>
